package game

import (
	"time"
	"math"
	"utils/log"
	"message"
	"utils"
	"math/rand"
)


//-------------------------------德州扑克游戏逻辑------------------------------
// 玩家押注 全押、跟注、加注都会调用此函数
func (self *Room) actionPlayerBet(player *User, money int) {
	log.Debug("actionPlayerBet: %v %v", player.str(), money)
	if money > player.money {
		money = player.money
	}
	player.money -= money

	if _, ok := self.bets[player.ID()]; ok {
		self.bets[player.ID()] += money
	} else {
		self.bets[player.ID()] = money
	}


	if v, ok := self.bets[player.ID()]; ok {
		log.Debug("玩家的押注: %v  %v", player.str(), v)
	}

	// 如果玩家所有钱都押了，判定为all-in，无论他选择的是跟注还是加注
	if player.money == 0 {
		player.state = message.PlayerState_STATE_ALL_IN
	}
}

// 玩家获得奖励
func (self *Room) actionPlayerAwardMoney(player *User, money int) {
	player.money += money // 给钱
	self.broadcastPlayerAttrChanged(player, message.PlayerAttributeType_PLAYER_ATTR_MONEY, player.money)
}

// 玩家开始行动
func (self *Room) actionPlayerThink(player *User) {
	log.Debug("actionPlayerThink: %v", player.str())
	self.currentThinkStartTime = time.Now().Unix()
	self.broadcastAction(player, message.PokerAction_ACTION_THINK, self.thinkTime)
	if player.bot {
		t := time.Duration(math.Max(1, float64(rand.Intn(MAX_BOT_THINK_TIME))))
		utils.Timer(t, func() {
			self.botAutoPlay()
		})
	}

	// # TODO 加入玩家超时机制，玩家一定时间没有行动自动弃牌处理
}

// 玩家思考超时
func (self *Room) actionPlayerThinkTimeout() {
	// 超时统一按弃牌处理
	self.actionFold(self.getCurrentPlayer())

	//处理下一个玩家
	self.nextPlayer()
}

// 获取所有奖池金额
func (self *Room) getAllPots() int {
	value := 0
	for _, v := range self.pots {
		value += v.value
	}
	return value
}

// 下一个玩家行动，如果是bot，则等待随机时间，直接作出结果
func (self *Room) nextPlayer() {
	// 每个玩家行动完都判定一下回合是否结束
	if self.isRoundFinished() {
		log.Debug("round finished")

		// 回合结束，则把玩家的筹码归到底池中，这个过程中可能产生新的边池，后续的押注操作都是针对最新的边池
		self.addBetsToPot()

		// 通知客户端奖池数据改变
		self.broadcastPotChanged()

		if len(self.playersNotFold()) <= 1 {
			// 只剩下一个没弃牌的，这个人直接赢了
			self.processPostRiver()
		} else if len(self.playersPlaying()) <= 1 {
			// 能正常比赛的都没了，除了弃牌的就是all-in的，快速进行比赛，最后比较结果
			for self.round != message.PokerRound_ROUND_POST_RIVER {
				self.nextRound()
			}
		} else {
			// 所有押注相等，进入下一个回合
			self.nextRound()
		}
		return
	}

	pred := func(x *User) bool {
		return x != nil && x.state == message.PlayerState_STATE_PLAYING
	}
	// 继续下一个玩家的行动
	self.position = self.getNextPosition(self.position, pred)

	// 都走了一轮了
	if self.position == self.lastToTalkSeat {
		self.firstBettingPass = false
	}

	// TODO 实现自动处理的逻辑   自动处理只有本回合有效，到下一回合自动清理
	// 自动弃牌 自动让牌 自动跟XX  跟任何注  这几个状态是互斥的，同时只有一个有效
	player := self.getCurrentPlayer()
	log.Debug("nextPlayer: %v %v", player.str(), self.position)

	if player == nil {
		log.Error("无法获取下一个玩家")
		return
	}

	if player.bot {
		// 机器人模拟思考
		self.actionPlayerThink(player)
	} else {
		if !self.playerAutoAction(player) {
			// 通知玩家开始思考，手动行动
			self.actionPlayerThink(player)
		}
	}
}

func (self *Room) playerAutoAction(player *User) bool {
	var auto message.PokerAction
	switch {
	case player.autoCheckOrFold:
		// 自动让牌或者弃牌
		if self.canCheck(player) {
			auto = message.PokerAction_ACTION_CHECK
		} else if self.canFold(player) {
			auto = message.PokerAction_ACTION_FOLD
		}
	case player.autoFold && self.canFold(player):
		// 自动弃牌
		auto = message.PokerAction_ACTION_FOLD
	case player.autoCall && self.canCall(player):
		// 自动让牌
		auto = message.PokerAction_ACTION_CALL
	case player.autoCallAny && self.canRaise(player):
		// 跟任何注
		auto = message.PokerAction_ACTION_RAISE
	}

	switch auto {
	case message.PokerAction_ACTION_CHECK:
		self.actionCheck(player)
	case message.PokerAction_ACTION_FOLD:
		self.actionFold(player)
	case message.PokerAction_ACTION_CALL:
		self.actionCall(player)
	case message.PokerAction_ACTION_RAISE:
		// 最小加注
		self.actionRaise(player, 0)
	default:
		return false
	}
	return true
}

// 小盲注
func (self *Room) actionSmallBlind(player *User) {
	log.Debug("actionSmallBlind:  %v", player.str())
	self.actionPlayerBet(player, self.smallBlind)
	self.broadcastAction(player, message.PokerAction_ACTION_SMALL_BLIND, self.smallBlind)
}

// 大盲注
func (self *Room) actionBigBlind(player *User) {
	log.Debug("actionBigBlind:  %v", player.str())
	self.actionPlayerBet(player, self.bigBlind)
	self.broadcastAction(player, message.PokerAction_ACTION_BIG_BLIND, self.bigBlind)
}

// 获取玩家此回合的押注
func (self *Room) getBet(player *User) int {
	if player == nil {
		return 0
	}
	if v, ok := self.bets[player.ID()]; ok {
		return v
	}
	return 0
}

//  是否可以行动
func (self *Room) canAct(player *User) bool {
	// 这些回合无法行动
	switch self.round {
	case message.PokerRound_ROUND_NONE: fallthrough
	case message.PokerRound_ROUND_POST_RIVER: fallthrough
	case message.PokerRound_ROUND_START:
		return false
	}

	if player.seat != self.position {
		log.Error("不是当前行动的玩家，不能行动:  %v  %v", player.seat, self.position)
	}

	// 只有当前行动的玩家是可以叫注的
	return player.seat == self.position
}

// 是否可以跟注 当别人的押注更大的时候，可以跟注，注意不能直接用上一个玩家的押注，因为上一个玩家可能是all-in，其押注金额可能更小
func (self *Room) canCall(player *User) bool {
	//log.Debug("%v canCall:  %v  %v", player.str(), self.highestBetNotFold(), self.getBet(player))
	return self.highestBetNotFold() > self.getBet(player)
}

// 是可以加注 玩家加注有一个最小限制，玩家必须有足够的钱加注
func (self *Room) canRaise(player *User) bool {
	minRaise, _, toCall := self.betLimits(player)
	//	# 如果玩家是最高押注，则可以加注（另一个选择是过牌）
	//	# 如果玩家不是最高押注，则玩家的钱要比最低押注金额大，最低押注金额是call的基础上，再加上上个玩家的加注
	//	# 玩家金钱不足的时候可以选择all-in   all-in是不用管金额大小的
	//log.Debug("%v   canRaise: %v  %v", player.str(), player.money, minRaise)
	return toCall == 0 || player.money >= minRaise
}

// 是否可以过牌 没有人加注，则可以过牌 场上最高的押注都比自己小，则可以让牌
func (self *Room) canCheck(player *User) bool {
	//log.Debug("%v  canCheck:  %v  %v", player.str(), self.highestBetNotFold(), self.getBet(player))
	return self.highestBetNotFold() <= self.getBet(player)
}

// 是否可以弃牌 玩家在比赛中并且没有全下或者弃牌才可以执行弃牌操作
func (self *Room) canFold(player *User) bool {
	return player.state == message.PlayerState_STATE_PLAYING
}

// 是否可以all-in
func (self *Room) canAllIn(player *User) bool {
	return player.state == message.PlayerState_STATE_PLAYING
}

// 弃牌
func (self *Room) actionFold(player *User) {
	log.Debug("玩家弃牌: %v", player.str())
	if !self.canAct(player) || !self.canFold(player) {
		log.Error("%v  不能弃牌", player.str())
		return
	}

	player.state = message.PlayerState_STATE_FOLD
	player.action = message.PokerAction_ACTION_FOLD
	player.actionParam = 0
	self.broadcastAction(player, player.action, player.actionParam)
	self.nextPlayer()
}

// 强制弃牌（玩家中途离席的情况）
func (self *Room) actionForceFold(player *User) {
	log.Debug("actionForceFold: %v", player.str())
	player.state = message.PlayerState_STATE_FOLD
	player.action = message.PokerAction_ACTION_FOLD
	player.actionParam = 0
	self.broadcastAction(player, player.action, player.actionParam)
}

// 让牌
func (self *Room) actionCheck(player *User) {
	log.Debug("玩家过牌: %v", player.str())
	if !self.canAct(player) || !self.canCheck(player) {
		log.Error("%v 不能过牌", player.str())
		return
	}

	player.action = message.PokerAction_ACTION_CHECK
	player.actionParam = 0
	self.broadcastAction(player, player.action, player.actionParam)
	self.nextPlayer()
}

// 跟注
func (self *Room) actionCall(player *User) {
	log.Debug("玩家跟注:  %v", player.str())
	if !self.canAct(player) || !self.canCall(player) {
		log.Error("%v 不能跟注", player.str())
		return
	}

	_, _, toCall := self.betLimits(player)
	if toCall == 0 {
		log.Error("还没有玩家押注，不能跟牌")
		return
	}

	player.action = message.PokerAction_ACTION_CALL
	player.actionParam = toCall
	self.actionPlayerBet(player, toCall)
	self.broadcastAction(player, player.action, player.actionParam)
	self.nextPlayer()
}

// 加注
func (self *Room) actionRaise(player *User, value int) {
	log.Debug("玩家加注: %v", player.str())
	if !self.canAct(player) || !self.canRaise(player) {
		log.Error("%v 不能加注", player.str())
		return
	}

	minRaise, maxRaise, _ := self.betLimits(player)
	if value < minRaise {
		value = minRaise
	}
	if value > maxRaise {
		value = maxRaise
	}
	if value == 0 {
		log.Error("加注错误:  %v  %v", player.str(), value)
	}

	lastHighestBet := self.highestBetNotFold()
	player.action = message.PokerAction_ACTION_RAISE
	player.actionParam = value
	self.actionPlayerBet(player, value)

	// 记录加注金额
	self.lastRaise = int(math.Max(float64(self.lastRaise), float64(self.highestBetNotFold() - lastHighestBet)))
	self.broadcastAction(player, player.action, player.actionParam)
	self.nextPlayer()
}

// 全压
func (self *Room) actionAllIn(player *User) {
	log.Debug("玩家all-in: %v", player.str())

	if !self.canAct(player) || !self.canAllIn(player) {
		log.Error("%v 无法all-in", player.str())
		return
	}

	player.state = message.PlayerState_STATE_ALL_IN
	player.action = message.PokerAction_ACTION_ALL_IN
	player.actionParam = player.money
	self.actionPlayerBet(player, player.money)
	self.broadcastAction(player, player.action, player.actionParam)
	self.nextPlayer()
}

// 玩家购买筹码 TODO 也需要广播
func (self *Room) actionReBuy(player *User, amount int) {
	if player == nil {
		return
	}

	// 大于最大购买
	if player.money + amount > self.maxMoney {
		return
	}

	// 小于最小购买
	if player.money + amount < self.minMoney {
		return
	}

	//	# 正在玩的时候是不能重新购买筹码的(否则所有玩家都可以无限加钱，无限all-in) 只有未开始的时候可以补充筹码
	//	# TODO 处理为任何时候都可以补充筹码，但是只有下一局才有效
	if self.round != message.PokerRound_ROUND_NONE {
		return
	}
	// 增加游戏中的筹码  TODO 广播通知玩家购买筹码
	player.money += amount

	// 减去实际金钱
	player.data.TotalMoney -= int64(amount)
}

// 押注限制 跟注和加注都会考虑到一些细节些情况
func (self *Room) betLimits(player *User) (int, int, int) {
	if self.round == message.PokerRound_ROUND_NONE {
		return 0, 0, 0
	}

	highestBet := self.highestBetNotFold()
	money := player.money
	bet := self.getBet(player)    // 玩家本回合的押注，当因为加注而本回合多次叫注时有效
	highestBetDiff := highestBet - bet// 这个是玩家自己的跟注金额，即玩家再押这么多就跟其他玩家持平（回合结束条件是所有玩家都持平）

	// 有限注的最大押注

	//	# 玩家的跟注是场上最大的押注（不能直接取上一个押注，因为可能上一个人all-in了，其押注金额可能更小）
	//	# 同时此次叫注可能不是本回合第一轮叫注，之前已经押过注，则跟注只要保证本回合押注跟最高玩家相等即可，即再次押注的是一个差额
	toCall := int(math.Min(float64(money), math.Max(float64(self.bigBlind - bet), float64(highestBetDiff))))

	// 玩家的最小加注是跟注金额基础之上额外一个大盲注或上一个玩家的加注金额
	minRaise := int(math.Min(float64(money), float64(highestBetDiff) + math.Max(float64(self.lastRaise), float64(self.bigBlind))))

	maxRaise := 0
	// 玩家的最大加注是玩家的全部钱（无限注），或者是一倍底池（有限注）
	if self.limitBet > 0 {
		//		# 有限注
		//	# 如果是有限注的话，最多不能超过底池的n倍，如1倍底池有限注(TODO 未测试，先实现无限注)
		maxBet := 0
		pot := self.getAllPots()
		for _, v := range self.bets {
			pot += v
		}
		maxBet = pot + highestBetDiff
		maxRaise = int(math.Min(float64(money), float64(maxBet + highestBetDiff)))
	} else {
		maxRaise = money

	}


	// # 返回 最小加注、最大加注、跟注
	return minRaise, maxRaise, toCall
}
